package com.by.gateway.config;

import com.by.rpc.registry.RedisServiceRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;

/**
 * 网关服务降级管理器
 * 实现服务熔断和降级功能
 */
@Component
public class CircuitBreakerManager {
    
    @Autowired(required = false)
    private RedisServiceRegistry redisServiceRegistry;
    
    // 本地缓存服务状态，减少Redis访问次数
    private final Map<String, ServiceStatus> serviceStatusCache = new ConcurrentHashMap<>();
    
    // 最大失败次数阈值
    private static final int MAX_FAILURE_COUNT = 3;
    
    // 服务降级超时时间（秒）
    private static final int DEGRADE_TIMEOUT_SECONDS = 60;
    
    /**
     * 记录服务调用失败
     * @param serviceName 服务名称
     */
    public void recordFailure(String serviceName) {
        // 更新本地缓存
        ServiceStatus status = serviceStatusCache.computeIfAbsent(serviceName, 
            k -> new ServiceStatus(serviceName));
        status.incrementFailureCount();
        
        // 如果失败次数超过阈值，则标记服务为不可用
        if (status.getFailureCount() >= MAX_FAILURE_COUNT) {
            status.setAvailable(false);
            // 将状态同步到Redis
            if (redisServiceRegistry != null) {
                redisServiceRegistry.markServiceAsUnavailable(
                    serviceName, status.getFailureCount(), DEGRADE_TIMEOUT_SECONDS);
            }
        }
    }
    
    /**
     * 记录服务调用成功
     * @param serviceName 服务名称
     */
    public void recordSuccess(String serviceName) {
        // 重置失败计数器
        ServiceStatus status = serviceStatusCache.computeIfAbsent(serviceName, 
            k -> new ServiceStatus(serviceName));
        status.resetFailureCount();
        status.setAvailable(true);
        
        // 清除Redis中的服务状态标记
        if (redisServiceRegistry != null) {
            redisServiceRegistry.clearServiceStatus(serviceName);
        }
    }
    
    /**
     * 检查服务是否可用
     * @param serviceName 服务名称
     * @return 服务是否可用
     */
    public boolean isServiceAvailable(String serviceName) {
        // 先检查本地缓存
        ServiceStatus status = serviceStatusCache.get(serviceName);
        if (status != null) {
            // 如果本地缓存标记为不可用，直接返回false
            if (!status.isAvailable()) {
                return false;
            }
        }
        
        // 检查Redis中的服务状态
        if (redisServiceRegistry != null) {
            boolean redisAvailable = redisServiceRegistry.isServiceAvailable(serviceName);
            if (!redisAvailable) {
                // 如果Redis中标记为不可用，更新本地缓存
                if (status == null) {
                    status = new ServiceStatus(serviceName);
                    serviceStatusCache.put(serviceName, status);
                }
                status.setAvailable(false);
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * 获取服务失败次数
     * @param serviceName 服务名称
     * @return 失败次数
     */
    public int getServiceFailureCount(String serviceName) {
        ServiceStatus status = serviceStatusCache.get(serviceName);
        if (status != null) {
            return status.getFailureCount();
        }
        
        // 从Redis获取失败次数
        if (redisServiceRegistry != null) {
            return redisServiceRegistry.getServiceFailureCount(serviceName);
        }
        
        return 0;
    }
    
    /**
     * 服务状态内部类
     */
    private static class ServiceStatus {
        private final String serviceName;
        private int failureCount;
        private boolean available;
        
        public ServiceStatus(String serviceName) {
            this.serviceName = serviceName;
            this.failureCount = 0;
            this.available = true;
        }
        
        public void incrementFailureCount() {
            this.failureCount++;
        }
        
        public void resetFailureCount() {
            this.failureCount = 0;
        }
        
        public int getFailureCount() {
            return failureCount;
        }
        
        public boolean isAvailable() {
            return available;
        }
        
        public void setAvailable(boolean available) {
            this.available = available;
        }
        
        public String getServiceName() {
            return serviceName;
        }
    }
}